/**
 * @file    LogHandlers.h
 * @brief   Include wosh::LogHandler interface and some built-in implementations.
 ****************************************************************************
 * @version 0.8.384 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_LogHandlers_H__
 #define __WOSH_Core_LogHandlers_H__

 #include <woshDefs.h>
 #include <core/LogContext.h>
 #include <core/Utilities.h> // multiplexer handler
 #include <stdarg.h>
 #include <stdio.h> //plaintext handler


namespace wosh {
 class SystemLogger;
 template <class T> class DataFieldCollector;

#define _Log_ENDLINE			"\n"

/**
 * @class   wosh::LogHandler
 * @brief   Base (abstract) class of a generic log-output renderer.
 *
 * Implementations must be (fully) thread safe (reentrant).
 * 
 * Log entries are usually formatted as: <tt>LEVEL TIMESTAMP CONTEXT(+indentation) CONTENT</tt>
 *
 ****************************************************************************
 * @version 0.8.499 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class LogHandler : public virtual IReflection
 {
	WOSH_CLASSNAME(wosh::LogHandler)
	WOSH_CLASS_ABSTRACT(true)

	public:
		/**
		 * @brief  Default constructor. Assign ID and register instance to global list (reentrant).
		 * @see    LogHandler::getInstances()
		 */
		LogHandler();
		/**
		 * @brief  Destructor. Unregister instance from global list (reentrant)
		 * @see    skip_unregister_instance
		 * @see    LogHandler::getInstances()
		 */
		virtual ~LogHandler();

	public:
		/**
		 * @brief   Log a generic message (using same format as printf). Reentrant.
		 * @param context [in] c string representing the log-context, may be NULL
		 * @param level [in] LogLevel of the message
		 * @param format [in] c string that contains the text to be written
		 * @param args [in] variable-arguments parameter
		 */
		virtual void log( const char* context, LogLevel level, const char* format, va_list args ) = 0;

	public:
		/**
		 * @brief   Open the output (device) for writing. Reentrant.
		 * @return  WRET_OK on success; a custom error code else
		 */
		virtual WRESULT open() = 0;
		/**
		 * @brief   Close the output (device). Reentrant.
		 * @return  WRET_OK on success; a custom error code else
		 */
		virtual WRESULT close() = 0;
		/**
		 * @brief   Force writing the buffer to the output (device). Reentrant.
		 * @return  WRET_OK on success; a custom error code else
		 * @note    really works only for buffered device/wrappers
		 */
		virtual WRESULT flush() = 0;

	public:
		/**
		 * @brief   Evaluate if the output (device) is open for writing. Reentrant.
		 * @return  true when the device is ready and valid for writing; false else
		 * @note    open()
		 */
		virtual bool isOpen() const = 0;
		/**
		 * @brief   Set the current protocol string: a standard way to reconfigure handler.
		 * @param protocol_string [in] literal string representing log-handler type and its settings, separator is '://'
		 * @return  WRET_OK on success; a custom error code else
		 * @note    
		 */
		virtual WRESULT setProtocol( const std::string& protocol_string ) = 0;
		/**
		 * @brief   Get the current protocol string.
		 * @return  literal string representing log-handler type and its settings, separator is '://'
		 */
		virtual std::string getProtocol() const = 0;

//@}
/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Get ID of the handler.
		 * @return  unique ID of the handler
		 */
		inline long getID() const					{ return this->id; }

		static DataFieldCollector<LogHandler>* getDataFields();
//@}
	protected:
		/**
		 * @brief   Render current (local) timestamp to internal buffer.
		 * @param format [in] format of the output
		 * @see     localtime()
		 * @see     strftime()
		 */
		void bufferTime( const char* format );

	protected:
		long id;					///< unique id of the handler (random positive long)
		MutexRW configMux;			///< mutex of previous vars
		char bufferDateTime[32];	///< buffer of the timestamp
		Mutex writeMux;				///< mutex of the write operation

		/**
		 * @brief   Get instances of LogHandler implementations. Meyer singleton.
		 * @return  static reference to instances' list. Allocated on first use (reentrant after that).
		 */
		static ListT<LogHandler*>& getInstances();

	private:
		bool skip_unregister_instance;	///< when true it won't unregister instance on destructor, used *only* by SystemLogger

		friend class wosh::SystemLogger;

}; // class def

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @class   wosh::LogHandlerPlainText
 * @brief   Render log entries to plain text file (written on local host).
 *          
 * All methods of this class are reentrant.
 *
 * Default output filename is \c default.log.
 *
 * Protocol is \c file://FILENAME.
 *
 *
 ****************************************************************************
 * @version 0.8.499 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class LogHandlerPlainText : public LogHandler
 {
	WOSH_CLASSNAME(wosh::LogHandlerPlainText)
	WOSH_CLASS_ABSTRACT(false)

	public:
		/**
		 * @brief  Default constructor. Initialize vars.
		 */
		LogHandlerPlainText()
			: LogHandler(), outFile(NULL), outFilename("default.log"), appendMode(true) { }
		/**
		 * @brief  Destructor. Close the output (device) when still openend.
		 */
		virtual ~LogHandlerPlainText() {
			close();
		 }

	public:
		virtual void log( const char* context, LogLevel level, const char* format, va_list args );

	public:
		virtual WRESULT open();
		virtual WRESULT close();
		virtual WRESULT flush();

	public:
		virtual bool isOpen() const;

		/**
		 * @brief  Configure the handler through protocol string. Reentrant.
		 * @param protocol_string [in] literal string representing new settings, prefix must be <tt>file://</tt>. Example: 'file://mylog.log'
		 * @return WRET_ERR_PARAM when prefix is missing or invalid; setFileName() return codes else
		 * @see    setFileName()
		 */
		virtual WRESULT setProtocol( const std::string& protocol_string );

		virtual std::string getProtocol() const;

/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Get current output filename. Reentrant.
		 * @return  string representing the filename
		 */
		inline std::string getFileName() const {
			MutexLockerRead mL(&this->configMux);
			return this->outFilename;
		 }
//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Set current output filename. When handler is opened it will be closed and re-opened. Reentrant.
		 * @param filename [in] string representing the filename
		 * @param append [in] when true it will open file for appending; else file will be replaced
		 * @return  WRET_OK on success; WRET_ERR_INTERNAL if log was opened and close() returned an error; 
		 *          WRET_ERR_ILLEGAL_USE if log can't be opened
		 */
		WRESULT setFileName( const std::string& filename, bool append = true );
//@}
	protected:
		WRESULT open_();
		WRESULT close_();

	protected:
		FILE* outFile;			///< reference of the current output FILE, may be NULL
		std::string outFilename;	///< current output filename
		bool appendMode;			///< file-mode flag, refer to \c a/w parameters of fopen

}; // class def

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @class   wosh::LogHandlerStdOut
 * @brief   Render log entries to stdout (default console output).
 *          
 * The implementation is very similar to wosh::LogHandlerPlainText,
 * but has been customized for \c stdout features.
 * 
 * All methods of this class are reentrant.
 *
 * On Windows and POSIX systems, outpur may be coloured (depends on LogLevel).
 *
 ****************************************************************************
 * @version 0.8.499 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class LogHandlerStdOut : public LogHandler
 {
	WOSH_CLASSNAME(wosh::LogHandlerStdOut)
	WOSH_CLASS_ABSTRACT(false)

	public:
		/**
		 * @brief  Default constructor. Initialize vars, set (default) output to \c stderr.
		 * @note   This handler should be allocated (internally) calling getInstance()
		 * @see    LogHandlerStdOut::getInstance()
		 */
		LogHandlerStdOut()
			: LogHandler(), outStd(stderr), printColoured(false) { }
		/**
		 * @brief  Destructor. Close the output (device) when still openend.
		 */
		virtual ~LogHandlerStdOut() {
			close();
		 }

	public:
		/**
		 * @brief  Get the singleton instance. Meyer singleton.
		 * @note   Static reference is allocated on first use
		 */
		static LogHandlerStdOut* getInstance();

	public:
		void log( const char* context, LogLevel level, const char* format, va_list args );

	public:
		WRESULT open();
		WRESULT close()			{ return WRET_OK; }
		WRESULT flush()			{ return WRET_OK; }

	public:
		bool isOpen() const		{ return true; }

		/**
		 * @brief  Configure the handler through protocol string. Reentrant.
		 * @param protocol_string [in] literal string representing new settings, prefix must be <tt>std://</tt>. Examples: 'std://stdout', 'std://stderr'
		 * @return WRET_ERR_PARAM when prefix is missing or invalid; setFileName() return codes else
		 */
		WRESULT setProtocol( const std::string& protocol_string );

		std::string getProtocol() const;

/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Evaluate if the output is coloured. Reentrant.
		 * @return  true when messages are coloured; false else
		 */
		inline bool isColoured() const {
			MutexLockerRead mL(&this->configMux);
			return this->printColoured;
		 }
//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Enable/Disable coloured output. Reentrant.
		 * @param enabled [in] true when messages are coloured; false else
		 * @note    coloured output is actually available on POSIX and Windows platforms only.
		 */
		void setColoured( bool enabled = true );
//@}
	protected:
		WRESULT open_();

	protected:
		FILE* outStd;	///< reference to current output. since stdout/stderr are just FILE*
		bool printColoured;	///< flag, when true it will print coloured messages

}; // class def

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @class   wosh::LogHandlerMultiplexer
 * @brief   Multiplexer: forwards entries to multiple LogHandlers.
 *
 *
 ****************************************************************************
 * @version 0.8.499 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
template <int COUNT>
class LogHandlerMultiplexer : public LogHandler
 {
	WOSH_CLASSNAME(wosh::LogHandlerMultiplexer)
	WOSH_CLASS_ABSTRACT(false)

	public:
		LogHandlerMultiplexer()
			: LogHandler() {
			for( int i=0; i<COUNT; ++i ) { this->handlers[i] = NULL; }
		 }
		LogHandlerMultiplexer( LogHandler* handler0, LogHandler* handler1 = NULL, LogHandler* handler2 = NULL )
			: LogHandler() {
			for( int i=0; i<COUNT; ++i ) { this->handlers[i] = NULL; }
			if ( COUNT > 0 ) this->handlers[0] = handler0;
			if ( COUNT > 1 ) this->handlers[1] = handler1;
			if ( COUNT > 2 ) this->handlers[2] = handler2;
		 }
		/**
		 * @brief  Destructor. Close sub-handlers when still openend.
		 */
		virtual ~LogHandlerMultiplexer() {
			close();
		 }

	public:
		virtual void log( const char* context, LogLevel level, const char* format, va_list args ) {
			this->configMux.lockForRead();
			this->writeMux.lock();
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				this->handlers[i]->log( context, level, format, args );
			 }
			this->writeMux.unLock();
			this->configMux.unLock();
		 }

	public:
		virtual WRESULT open() {
			WRESULT ret = WRET_OK; int found = 0;
			MutexLockerWrite mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				++found;
				ret += this->handlers[i]->open();
			 }
			if ( ret != found )
				return WRET_ERR_INTERNAL;
			return ret;
		 }
		virtual WRESULT close() {
			WRESULT ret = WRET_OK; int found = 0;
			MutexLockerWrite mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				++found;
				ret += this->handlers[i]->close();
			 }
			if ( ret != found )
				return WRET_ERR_INTERNAL;
			return ret;
		 }
		virtual WRESULT flush() {
			WRESULT ret = WRET_OK; int found = 0;
			MutexLockerWrite mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				++found;
				ret += this->handlers[i]->flush();
			 }
			if ( ret != found )
				return WRET_ERR_INTERNAL;
			return ret;
		 }

	public:
		virtual bool isOpen() const {
			int ret = 0; int found = 0;
			MutexLockerRead mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				++found;
				if ( !this->handlers[i]->isOpen() ) continue;
				++ret;
			 }
			return (ret != 0 && ret == found );
		 }

		virtual WRESULT setProtocol( const std::string& ) {
			return WRET_ERR_INTERNAL;
		 }

		virtual std::string getProtocol() const {
			MutexLockerRead mL(&this->configMux);
			int found = 0;
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				++found;
			 }
			return Utilities::format("multiplex[%d]://", found);
		 }

	public:
		WRESULT addHandler( LogHandler* handler ) {
			MutexLockerWrite mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] != NULL ) continue;
				this->handlers[i] = handler;
				return WRET_OK;
			 }
			return WRET_ERR_ILLEGAL_USE;
		 }
		WRESULT deleteHandler( LogHandler* handler ) {
			MutexLockerWrite mL(&this->configMux);
			for( int i=0; i<COUNT; ++i ) {
				if ( this->handlers[i] == NULL ) continue;
				if ( this->handlers[i] != handler ) continue;
				this->handlers[i] = NULL;
				return WRET_OK;
			 }
			return WRET_ERR_PARAM;
		 }

	protected:
		LogHandler* handlers[COUNT];	///< array of sub-handlers (log() is forwarded sequentially)

}; // class def

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * @class   wosh::DefaultSharedLogHandler
 * @brief   Default log handler, Meyer singleton class, provided for convenience.
 *
 * By default, it renders log to stderr and a local file, also provides two more empty slots.
 * 
 *
 ****************************************************************************
 * @version 0.8.499 $Id: LogHandlers.h 2710 2010-06-25 10:17:26Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
class DefaultSharedLogHandler : public LogHandlerMultiplexer<4>
 {
	WOSH_CLASSNAME(wosh::DefaultSharedLogHandler)
	WOSH_CLASS_ABSTRACT(false)

	public:
		/**
		 * @brief  Destructor. Delete sub-handlers.
		 * @warning Linked handlers will be destroyed!
		 */
		virtual ~DefaultSharedLogHandler();

	public:
		/**
		 * @brief  Get the singleton instance. Meyer singleton.
		 * @note   Static reference is allocated on first use
		 */
		static DefaultSharedLogHandler* getInstance();

/** @name Getters
 * @{
 ******************************************************************************/
	public:
		/**
		 * @brief   Get current output filename. Reentrant.
		 * @return  literal string represeting the current filename
		 */
		std::string getFileName() const;
//@}
/** @name Setters
 * @{
 ******************************************************************************/
	public:
		WRESULT setStdOut( bool enabled = true, bool coloured = true );
		WRESULT setFileName( const std::string& filename, bool append = true );
//@}
	private:
		DefaultSharedLogHandler()
			: LogHandlerMultiplexer<4>() {
			if ( _DefaultSharedLogHandler_STDERR_Enabled ) {
				addHandler( new LogHandlerStdOut() );
				open();
			 }
		 }
		DefaultSharedLogHandler( const DefaultSharedLogHandler& ) : LogHandlerMultiplexer<4>() { }
		DefaultSharedLogHandler& operator=( const DefaultSharedLogHandler&  ) { return *this; }

}; // class def

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh

#endif //__WOSH_Core_LogHandlers_H__
